﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FinMkts.Messaging.UnitTests
{
    [TestClass]
    public class AddressedMessageSerializerTests
    {
        readonly AddressedMessageSerializer _serializer = new AddressedMessageSerializer();
        
        [TestMethod, ExpectedException(typeof(ArgumentNullException))]
        public void cannot_serialize_null_message()
        {
            _serializer.Serialize(null);
        }
        
        [TestMethod, ExpectedException(typeof(ArgumentNullException))]
        public void cannot_serialize_message_with_null_topic()
        {
            _serializer.Serialize(new AddressedMessage{Topic=null, Body=new byte[1]});
        }

        [TestMethod, ExpectedException(typeof(ArgumentNullException))]
        public void cannot_serialize_message_with_empty_topic()
        {
            _serializer.Serialize(new AddressedMessage{Topic="", Body=new byte[1]});
        }

        [TestMethod, ExpectedException(typeof(ArgumentNullException))]
        public void cannot_serialize_message_with_null_body()
        {
            _serializer.Serialize(new AddressedMessage{Topic="a", Body=null});
        }

        [TestMethod]
        public void can_serialize_message_with_zero_size_body()
        {
            var result = _serializer.Serialize(new AddressedMessage { Topic = "a", Body = new byte[0] });
            Assert.IsNotNull(result);
            const int expectSize = 2 + 4 + 1;
            Assert.AreEqual(expectSize, result.Length);

            int offset = 0;
            var actual = _serializer.Deserialize(result, ref offset, expectSize);
            Assert.IsNotNull(actual);
            Assert.AreEqual("a", actual.Topic);
            Assert.IsNotNull(actual.Body);
            Assert.AreEqual(0, actual.Body.Length);
        }

        [TestMethod]
        public void can_serialize_message_with_body()
        {
            var message = "goodbye, and thanks for all the fish";
            var body = Encoding.UTF8.GetBytes(message);
            var expected = new AddressedMessage { Topic = "earth", Body = body };
            var result = _serializer.Serialize(expected);
            Assert.IsNotNull(result);
            int expectSize = 2 + 4 + Encoding.UTF8.GetByteCount(expected.Topic) + body.Length;
            Assert.AreEqual(expectSize, result.Length);

            int offset = 0;
            var actual = _serializer.Deserialize(result, ref offset, expectSize);
            Assert.IsNotNull(actual);
            Assert.AreEqual(expected.Topic, actual.Topic);
            Assert.IsNotNull(actual.Body);
            Assert.AreEqual(message, Encoding.UTF8.GetString(actual.Body));
        }

        [TestMethod]
        public void Deserialize_Returns_WhenManyMessagesInBuffer()
        {
            var message = "goodbye, and thanks for all the fish";
            var body = Encoding.UTF8.GetBytes(message);
            var expected = new AddressedMessage { Topic = "earth", Body = body };
            byte[] serialized = _serializer.Serialize(expected);
            byte[] twoMessages = new byte[serialized.Length * 2];
            Buffer.BlockCopy(serialized, 0, twoMessages, 0, serialized.Length);
            Buffer.BlockCopy(serialized, 0, twoMessages, serialized.Length, serialized.Length);

            int offset = 0;
            var first = _serializer.Deserialize(twoMessages, ref offset, twoMessages.Length);
            Assert.IsNotNull(first);
            Assert.AreEqual(expected.Topic, first.Topic);
            Assert.IsNotNull(first.Body);
            Assert.AreEqual(message, Encoding.UTF8.GetString(first.Body));

            var second = _serializer.Deserialize(twoMessages, ref offset, twoMessages.Length);
            Assert.IsNotNull(second);
            Assert.AreEqual(expected.Topic, second.Topic);
            Assert.IsNotNull(second.Body);
            Assert.AreEqual(message, Encoding.UTF8.GetString(second.Body));

            var third = _serializer.Deserialize(twoMessages, ref offset, twoMessages.Length);
            Assert.IsNull(third);

        }


    }
}
